package com.yugao.fintech.draper.security.support;

import cn.hutool.extra.spring.SpringUtil;
import com.yugao.fintech.draper.core.constant.SecurityConstants;
import com.yugao.fintech.draper.security.SecurityUser;
import com.yugao.fintech.draper.security.service.SecurityUserDetailsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;

import java.security.Principal;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {

    private final OAuth2AuthorizationService oAuth2AuthorizationService;

    /**
     * @param token the token to introspect
     * @return
     */
    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        // 根据token检索OAuth2Authorization
        OAuth2Authorization authorization = oAuth2AuthorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        if (Objects.isNull(authorization)) {
            throw new InvalidBearerTokenException(token);
        }

        if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(authorization.getAuthorizationGrantType())) {
            return new DefaultOAuth2AuthenticatedPrincipal(authorization.getPrincipalName(),
                    authorization.getAttributes(), AuthorityUtils.NO_AUTHORITIES);
        }

        // 筛选出支持此客户端的UserDetailsService
        Optional<SecurityUserDetailsService> optional = SpringUtil.getBeansOfType(SecurityUserDetailsService.class)
                .values()
                .stream()
                .filter(service -> service.support(Objects.requireNonNull(authorization).getRegisteredClientId()))
                .max(Comparator.comparingInt(Ordered::getOrder));

        UserDetails userDetails = null;
        try {
            Object principal = Objects.requireNonNull(authorization).getAttributes().get(Principal.class.getName());
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
            Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
            userDetails = optional.get().loadUserByUser((SecurityUser) tokenPrincipal);
        } catch (UsernameNotFoundException notFoundException) {
            log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());
            throw notFoundException;
        } catch (Exception ex) {
            log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
        }

        // 注入扩展属性,方便上下文获取客户端ID
        SecurityUser user = (SecurityUser) userDetails;
        Objects.requireNonNull(user)
                .getAttributes()
                .put(SecurityConstants.CLIENT_ID, authorization.getRegisteredClientId());
        return user;
    }
}
